package com.idol.factory;

import com.idol.annotation.Autowired;
import com.idol.annotation.Service;
import com.idol.annotation.Transaction;
import com.idol.controller.MyHttpServlet;
import com.idol.proxy.BeanProxyFactory;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;

/**
 * @author Supreme_Sir
 * @version 1.0
 * @className BeanScanner
 * @description
 * @date 2020/10/13 15:26
 **/
public class BeanScanner {
    private static final String EXT = "class";
    private static final Map<String, List<Field>> FIT_FIELD_MAP = new HashMap<>(16);
    private static final Map<String, List<Method>> ENHANCER_METHOD_MAP = new HashMap<>(16);

    /**
     * 根据包名获取包的URL
     *
     * @param packageName com.idol
     * @return
     */
    private static String getPackagePath(String packageName) throws UnsupportedEncodingException {
        String pkgDirName = packageName.replace(".", File.separator);
        URL url = Thread.currentThread().getContextClassLoader().getResource(pkgDirName);
        return url == null ? null : URLDecoder.decode(url.getFile(), "UTF-8");
    }

    /**
     * 遍历指定目录下所有扩展名为class的文件
     */
    private static List<File> getAllClassFile(String strPath, List<File> fileList) {
        File dir = new File(strPath);
        // 该文件目录下文件全部放入数组
        File[] files = dir.listFiles();
        if (files != null) {
            for (int i = 0; i < files.length; i++) {
                String fileName = files[i].getName();
                // 判断是文件还是文件夹
                if (files[i].isDirectory()) {
                    // 获取文件绝对路径
                    getAllClassFile(files[i].getAbsolutePath(), fileList);
                    // 判断文件名是否以.avi结尾
                } else if (fileName.endsWith(EXT)) {
                    fileList.add(files[i]);
                } else {
                    continue;
                }
            }

        }
        return fileList;
    }

    /**
     * 加载类
     *
     * @param file
     * @param packagePath
     * @param packageName
     * @return
     * @throws ClassNotFoundException
     */
    private static void initObjects(File file, String packagePath, String packageName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        // 考虑class文件在子目录中的情况
        String absPath = file.getAbsolutePath().substring(0, file.getAbsolutePath().length() - EXT.length() - 1);
        String className = absPath.substring(packagePath.length()).replace(File.separatorChar, '.');
        className = className.startsWith(".") ? packageName + className : packageName + "." + className;
        Class<?> clazz = Class.forName(className);
        if (canBeInit(clazz)) {
            registAutowaredField(clazz);
            registTranscationMethod(clazz);
            Class<?> loadClass = Thread.currentThread().getContextClassLoader().loadClass(className);
            String[] split = className.split("\\.");
            BeanContainer.getBeanContainer().put(split[split.length - 1], className, loadClass.newInstance());
        }
    }

    /**
     * 为 @Autowared 注入装配对象
     *
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private static void fitField() throws NoSuchFieldException, IllegalAccessException {
        for (Map.Entry entry : FIT_FIELD_MAP.entrySet()) {
            Object bean = BeanContainer.getBeanContainer().get((String) entry.getKey());
            List<Field> fields = (List<Field>) entry.getValue();
            for (Field field : fields) {
                Field declaredField = bean.getClass().getDeclaredField(field.getName());
                declaredField.setAccessible(true);
                declaredField.set(bean, BeanContainer.getBeanContainer().get(declaredField.getType().toString().split(" ")[1]));
            }
        }
        FIT_FIELD_MAP.clear();
    }

    /**
     * 登记需要自动装配的属性
     *
     * @param clazz
     */
    private static void registAutowaredField(Class<?> clazz) {
        String name = clazz.getName();
        Field[] fields = clazz.getDeclaredFields();
        List<Field> fieldList = new ArrayList<>();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Autowired.class)) {
                fieldList.add(field);
            }
        }
        FIT_FIELD_MAP.put(name, fieldList);
    }

    /**
     * 登记需要进行增强的方法
     *
     * @param clazz
     */
    private static void registTranscationMethod(Class<?> clazz) {
        if (clazz.isAnnotationPresent(Service.class)) {
            String name = clazz.getName();
            Method[] methods = clazz.getDeclaredMethods();
            List<Method> methodList = new ArrayList<>();
            for (Method method : methods) {
                if (method.isAnnotationPresent(Transaction.class)) {
                    methodList.add(method);
                }
            }
            ENHANCER_METHOD_MAP.put(name, methodList);
        }
    }

    /**
     * 判断当前类是否符合实例化条件
     *
     * @param clazz
     * @return
     */
    private static boolean canBeInit(Class<?> clazz) {
        boolean result = false;
        if (!clazz.isInterface()) {
            if (clazz.getSuperclass() != MyHttpServlet.class) {
                Constructor<?>[] constructors = clazz.getConstructors();
                if (constructors.length > 0) {
                    for (Constructor<?> constructor : constructors) {
                        if (constructor.getParameters().length < 1) {
                            return true;
                        }
                    }
                }
            }
        }
        return result;
    }

    /**
     * 创建代理对象，开启数据库事务
     */
    private static void createProxy() {
        BeanProxyFactory proxyFactory = (BeanProxyFactory) BeanContainer.getBeanContainer().get("BeanProxyFactory");
        for (Map.Entry entry : ENHANCER_METHOD_MAP.entrySet()) {
            String beanContainerKey = (String) entry.getKey();
            Object object = BeanContainer.getBeanContainer().get(beanContainerKey);

            Class<?>[] interfaces = object.getClass().getInterfaces();
            if (interfaces.length > 0) {
                beanContainerKey = interfaces[0].toString().split(" ")[1];
            }
            object = proxyFactory.createTransactionProxy(object);

            BeanContainer.getBeanContainer().put(beanContainerKey, object);
        }
        ENHANCER_METHOD_MAP.clear();
    }

    /**
     * 扫描指定的包
     *
     * @param packageName
     * @throws UnsupportedEncodingException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws ClassNotFoundException
     * @throws NoSuchFieldException
     */
    public static void scanPackage(String packageName) throws UnsupportedEncodingException,
            IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchFieldException {
        String packagePath = getPackagePath(packageName);
        List<File> classFile = getAllClassFile(packagePath, new ArrayList<File>());
        for (File file : classFile) {
            initObjects(file, packagePath, packageName);
        }
        fitField();
        createProxy();
    }

}
